package com.atguigu.atcrowdfunding.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@EnableGlobalMethodSecurity(prePostEnabled = true)  //通过细粒度权限控制controller方法的访问，可以通过注解在controller方法上绑定权限
@EnableWebSecurity           //启用SpringSecurity权限验证功能
@Configuration //@Configuration 当前类是一个配置类组件
// Component , Repository , Service , Controller
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //授权
    //首页和静态资源登录页面所有人都可以访问
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()  //authorizeRequests() 开始对所有的请求进行授权
                //  permitAll() 授权所有人都可以访问    hasAnyRole("admin" , "boss"):授权拥有角色的用户才可以访问
                // hasAnyAuthority("user:add" , "user:del"):授权拥有权限的用户才可以访问
                //配置浏览器访问的路径
                .antMatchers("/index" , "/static/**" ,"/login.html").permitAll()// antMatchers(...)对传入参数的多个路径进行授权
//                .antMatchers("/admin/delete").hasAnyRole("GL - 组长")
                .anyRequest().authenticated();// anyRequest() 除了上面配置的其他没有配置的请求 ，authenticated() 需要被授权
        //权限框架默认启用CSRF，以后所有的post方式提交的表单请求必须携带csrf参数，否则springsecurity认为无权访问
        http.csrf().disable(); // 禁用csrf防跨站点攻击功能
        //登录请求的接管：
        http.formLogin()
                .loginPage("/login.html")//提交登录请求的页面
                .loginProcessingUrl("/doLogin") // 登录请求的地址
                .usernameParameter("loginacct") //登录账号参数的key
                .passwordParameter("userpswd") //登录密码参数的key
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                       //指定无论有没有登录之前的页面，登录成功后都重定向到自己设置的页面
                        httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/admin/main.html");
                    }
                });
                //springsecurity还会在登录成功后跳转回登录之前的页面
                //.defaultSuccessUrl("/admin/main.html");   //登录成功重定向访问的地址

        //注销请求的接管：
        http.logout()
                .logoutUrl("/logout") //注销请求的地址
                .logoutSuccessUrl("/login.html"); //注销成功后跳转的页面地址
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            //当用户访问到未授权页面时的处理方法
            @Override
            public void handle(HttpServletRequest req, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
//               判断请求是同步还是异步
                if("XMLHttpRequest".equals(req.getHeader("X-Requested-With"))){
                    //异步请求
                    httpServletResponse.getWriter().write("403");
                }else{
                    //同步请求
                    //设置错误消息
                    req.setAttribute("msg" , e.getMessage());
                    //转发到403页面给用户提示
                    req.getRequestDispatcher("/WEB-INF/pages/error/403.jsp").forward(req,httpServletResponse);
                }

                //如果是ajax提交的异步请求被拒绝，那么不应该转发到403页面给ajax
            }
        });
    }

    /**
     * 向spring容器中注入javabean对象的方式：
     *  1、在spring配置文件中通过<bean class=""></bean>
     *  2、在组件类名上使用@组件注解
     *  3、在组件类中通过@Bean注解标注到有返回值的方法上，该方法的返回值会自动注入到容器中
     */
    @Bean
    public PasswordEncoder getPasswordEncoder(){
       // return  new MessageDigestPasswordEncoder("md5");
        return new BCryptPasswordEncoder();
    }
    @Autowired
    UserDetailsService userDetailsService;
    //认证:主体创建过程
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);
        //在内存中写死主体信息
//        auth.inMemoryAuthentication()
//                .withUser("lijie").password("e10adc3949ba59abbe56e057f20f883e").authorities("user:query","user:del")
//                .and()
//                .withUser("zhouchang").password("123457").roles("ADMIN","ZUZHANG");
        //userDetailsService()  用来给springsecurity配置一个主体创建的业务类对象
        // 以后的rbac的表名称结构字段 类型等等都可能不一样
        auth.userDetailsService(userDetailsService);

    }
}
